home *** CD-ROM | disk | FTP | other *** search
/ Mac Magazin/MacEasy 11 / Mac Magazin and MacEasy Magazine CD - Issue 11.iso / Sharewarebibliothek / Entwickler / WASTE 1.1 C / WEHighLevelEditing.c < prev    next >
Text File  |  1995-05-25  |  31KB  |  1,236 lines

  1. // { WASTE PROJECT: }
  2. // { High-Level Editing Routines }
  3.  
  4. // { Copyright © 1993-1994 Marco Piovanelli }
  5. // { All Rights Reserved }
  6.  
  7. #include "WASTEIntf.h"
  8.  
  9. typedef struct DoubleByte {
  10.     char firstByte;
  11.     char secondByte;
  12. } DoubleByte;
  13.  
  14. pascal WEActionHandle WEGetActionStack(WEHandle hWE)
  15. {
  16.     return (WEActionHandle)(*hWE)->hActionStack;
  17. } // { WEGetActionStack }
  18.  
  19. pascal OSErr WEPushAction(WEActionHandle hAction)
  20. {
  21.     WEPtr pWE;
  22.     WEActionHandle hLast;
  23.  
  24.     // { find the last action in the given stack }
  25.     hLast = hAction;
  26.     while ((*hLast)->hNext != nil)
  27.         hLast = (*hLast)->hNext;
  28.  
  29.     // { prepend hAction in front of the action stack }
  30.     pWE = *((*hAction)->hOwner);
  31.     (*hLast)->hNext = (WEActionHandle)pWE->hActionStack;
  32.     pWE->hActionStack = (Handle)hAction;
  33.  
  34.     return noErr;
  35. } // { WEPushAction }
  36.  
  37. pascal OSErr WENewAction(long rangeStart, long rangeEnd, long newTextLength,
  38.                             WEActionKind actionKind, WEActionFlags actionFlags,
  39.                             WEHandle hWE, WEActionHandle *hAction)
  40. {
  41.     WEActionPtr pAction;
  42.     OSErr err;
  43.  
  44.     // { allocate a new action record }
  45.     err = _WEAllocate(sizeof(WEAction), kAllocClear, (Handle *)hAction);
  46.     if (err != noErr) 
  47.         goto cleanup1;
  48.  
  49.     // { lock it down }
  50.     HLock((Handle)*hAction);
  51.     pAction = **hAction;
  52.  
  53.     // { fill in the fields }
  54.     pAction->hOwner = hWE;
  55.     pAction->delRangeStart = rangeStart;
  56.     pAction->delRangeLength = newTextLength;
  57.     pAction->insRangeLength = rangeEnd - rangeStart;
  58.     pAction->actionKind = actionKind;
  59.     pAction->actionFlags = actionFlags;
  60.  
  61.     // { remember selection range }
  62.     WEGetSelection(&pAction->hiliteStart, &pAction->hiliteEnd, hWE);
  63.  
  64.     // { allocate a handle to hold the text to be saved, unless otherwise specified }
  65.     if ((actionFlags & weAFDontSaveText) == 0) 
  66.     {
  67.         err = _WEAllocate(0, kAllocTemp, &pAction->hText);
  68.         if (err != noErr) 
  69.             goto cleanup1;
  70.     }
  71.  
  72.     // { allocate a handle to hold the styles to be saved, unless otherwise specified }
  73.     if ((actionFlags & weAFDontSaveStyles) == 0) 
  74.     {
  75.         err = _WEAllocate(0, kAllocTemp, &pAction->hStyles);
  76.         if (err != noErr) 
  77.             goto cleanup1;
  78.     }
  79.  
  80.     // { allocate a handle to hold the "soup" to be saved, unless otherwise specified }
  81.     if ((actionFlags & weAFDontSaveSoup) == 0) 
  82.     {
  83.         err = _WEAllocate(0, kAllocTemp, &pAction->hSoup);
  84.         if (err != noErr) 
  85.             goto cleanup1;
  86.     }
  87.     
  88.     // { make a copy of text range }
  89.     err = WECopyRange(rangeStart, rangeEnd, pAction->hText, (Handle)pAction->hStyles,
  90.                     pAction->hSoup, hWE);
  91.     if (err != noErr)
  92.         goto cleanup1;
  93.  
  94.     // { unlock action record }
  95.     HUnlock((Handle)*hAction);
  96.  
  97.     // { skip clean-up section }
  98.     goto cleanup0;
  99.  
  100. cleanup1:
  101.     // { clean up }
  102.     _WEForgetHandle(&pAction->hText);
  103.     _WEForgetHandle(&pAction->hStyles);
  104.     _WEForgetHandle(&pAction->hSoup);
  105.     _WEForgetHandle((Handle *)hAction);
  106.  
  107. cleanup0:
  108.     // { return result code }
  109.     return err;
  110.  
  111. } // { WENewAction }
  112.  
  113. pascal void WEDisposeAction(WEActionHandle hAction)
  114. {
  115.     WEActionPtr pAction;
  116.     WEActionHandle hNext;
  117.  
  118.     while (hAction != nil)
  119.     {
  120.         // { lock the action record }
  121.         HLock((Handle)hAction);
  122.         pAction = *hAction;
  123.         hNext = pAction->hNext;
  124.  
  125.         // { throw away text, styles and soup }
  126.         _WEForgetHandle(&pAction->hText);
  127.         _WEForgetHandle(&pAction->hStyles);
  128.         _WEForgetHandle(&pAction->hSoup);
  129.  
  130.         // { throw away the action record itself }
  131.         DisposeHandle((Handle)hAction);
  132.  
  133.         // { repeat the same sequence with all linked actions }
  134.         hAction = hNext;
  135.  
  136.     } // { while }
  137. } // { WEDisposeAction }
  138.  
  139. pascal void WEForgetAction(WEActionHandle *hAction)
  140. {
  141.     WEActionHandle theAction;
  142.  
  143.     theAction = *hAction;
  144.     if (theAction != nil) 
  145.     {
  146.         *hAction = nil;
  147.         WEDisposeAction(theAction);
  148.     }
  149. } // { WEForgetAction }
  150.  
  151. pascal OSErr WEDoAction(WEActionHandle hAction)
  152. {
  153.     WEActionHandle hRedoAction;
  154.     WEActionPtr pAction;
  155.     WEHandle hWE;
  156.     WEPtr pWE;
  157.     long offset, delOffset, insOffset;
  158.     long redrawStart, redrawEnd;
  159.     Boolean saveActionLock, saveWELock, saveTextLock;
  160.     OSErr err;
  161.  
  162.     // { sanity check: make sure hAction isn't NIL }
  163.     if (hAction == nil) 
  164.     {
  165.         return nilHandleErr;
  166.     }
  167.     
  168.     // { get handle to associated WE instance }
  169.     hWE = (*hAction)->hOwner;
  170.  
  171.     // { lock the WE record }
  172.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  173.     pWE = *hWE;
  174.  
  175.     // { return an error code if this instance is read-only }
  176.     err = weReadOnlyErr;
  177.     if (BTST(pWE->flags, weFReadOnly))
  178.         goto cleanup;
  179.  
  180.     // { stop any ongoing inline input session }
  181.     WEStopInlineSession(hWE);
  182.  
  183.     // { hide selection highlighting and the caret }
  184.     _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  185.     if (BTST(pWE->flags, weFCaretVisible))
  186.         _WEBlinkCaret(hWE);
  187.  
  188.     redrawStart = LONG_MAX;
  189.     redrawEnd = 0;
  190.  
  191.     do
  192.     {
  193.  
  194.         // { lock the action record }
  195.         saveActionLock = _WESetHandleLock((Handle)hAction, true);
  196.         pAction = *hAction;
  197.         offset = pAction->delRangeStart;
  198.         delOffset = offset + pAction->delRangeLength;
  199.         insOffset = offset + pAction->insRangeLength;
  200.  
  201.         // { if undo support is enabled, save the range to be affected by this action }
  202.         if (BTST(pWE->flags, weFUndoSupport)) 
  203.         {
  204.             if (WENewAction(offset, delOffset, pAction->insRangeLength, pAction->actionKind,
  205.                 (pAction->actionFlags ^ weAFIsRedo), hWE, &hRedoAction) == noErr) 
  206.             {
  207.                 if (WEPushAction(hRedoAction) != noErr)
  208.                 {
  209.                     ;
  210.                 }
  211.             }
  212.         }
  213.         if (pAction->hText != nil) 
  214.         {
  215.  
  216.             // { delete the range to replace }
  217.             err = _WEDeleteRange(offset, delOffset, hWE);
  218.             if (err != noErr) 
  219.                 goto cleanup;
  220.  
  221.             // { insert the saved text }
  222.             saveTextLock = _WESetHandleLock(pAction->hText, true);
  223.             err = _WEInsertText(offset, *pAction->hText, pAction->insRangeLength, hWE);
  224.             _WESetHandleLock(pAction->hText, saveTextLock);
  225.             if (err != noErr) 
  226.                 goto cleanup;
  227.         }
  228.  
  229.         // { apply the saved styles, if any }
  230.         if (pAction->hStyles != nil) 
  231.         {
  232.             err = _WEApplyStyleScrap(offset, insOffset, (StScrpHandle)pAction->hStyles, hWE);
  233.             if (err != noErr) 
  234.                 goto cleanup;
  235.         }
  236.  
  237.         // { the same goes for the soup }
  238.         if (pAction->hSoup != nil) 
  239.         {
  240.             err = _WEApplySoup(offset, pAction->hSoup, hWE);
  241.             if (err != noErr) 
  242.                 goto cleanup;
  243.         }
  244.  
  245.         // { adjust redraw range }
  246.         if (offset < redrawStart) 
  247.             redrawStart = offset;
  248.         if (insOffset > redrawEnd) 
  249.             redrawEnd = insOffset;
  250.  
  251.         // { unlock action record }
  252.         _WESetHandleLock((Handle)hAction, saveActionLock);
  253.  
  254.         // { go to next action }
  255.         hAction = (*hAction)->hNext;
  256.     } while (hAction != nil);
  257.  
  258.     // { restore the original selection range }
  259.     pWE->selStart = pAction->hiliteStart;
  260.     pWE->selEnd = pAction->hiliteEnd;
  261.  
  262.     // { redraw the text }
  263.     err = _WERedraw(redrawStart, redrawEnd, hWE);
  264.     if (err != noErr) 
  265.         goto cleanup;
  266.  
  267.     // { clear result code }
  268.     err = noErr;
  269.  
  270. cleanup:
  271.     // { unlock the WE record }
  272.     _WESetHandleLock((Handle)hWE, saveWELock);
  273.  
  274.     // { return result code }
  275.     return err;
  276. } // { WEDoAction }
  277.  
  278. pascal OSErr WEUndo(WEHandle hWE)
  279. {
  280.     WEPtr pWE;
  281.     WEActionHandle hAction;
  282.     Boolean saveWELock;
  283.     OSErr retval;
  284.  
  285.     // { lock the WE record }
  286.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  287.     pWE = *hWE;
  288.     
  289.     // { "detach" the action stack from the WE instance }
  290.     hAction = (WEActionHandle)(pWE->hActionStack);
  291.     pWE->hActionStack = nil;
  292.  
  293.     if (hAction != nil)
  294.     {
  295.  
  296.         // { undoing a change _decrements_ the modification count; }
  297.         // { redoing the change increments it again }
  298.         if (((*hAction)->actionFlags & weAFIsRedo) != 0)
  299.             pWE->modCount = pWE->modCount + 1;
  300.         else
  301.             pWE->modCount = pWE->modCount - 1;
  302.  
  303.         // { perform the action... }
  304.         retval = WEDoAction(hAction);
  305.  
  306.         // { ...and throw it away }
  307.         WEDisposeAction(hAction);
  308.     }
  309.     else
  310.     {
  311.         // { return an error code if the undo buffer is empty }
  312.         retval = weCantUndoErr;
  313.     }
  314.  
  315.     // { unlock the WE record }
  316.     _WESetHandleLock((Handle)hWE, saveWELock);
  317.  
  318.     return retval;
  319. }
  320.  
  321. pascal void WEClearUndo(WEHandle hWE)
  322. {
  323.  
  324.     // { dispose of the action chain associated with the given WE instance }
  325.     WEForgetAction((WEActionHandle *)&(*hWE)->hActionStack);
  326. } // { WEClearUndo }
  327.  
  328. pascal WEActionKind WEGetUndoInfo(Boolean *redoFlag, WEHandle hWE)
  329. {
  330.     WEActionKind retval;
  331.     
  332.     retval = weAKNone;        // { assume no actions have been saved }
  333.     *redoFlag = false;
  334.  
  335.     if ((*hWE)->hActionStack != nil) 
  336.     {
  337.         retval = (*((WEActionHandle)(*hWE)->hActionStack))->actionKind;
  338.         *redoFlag = (((*((WEActionHandle)(*hWE)->hActionStack))->actionFlags & weAFIsRedo) != 0);
  339.     }
  340.     return retval;
  341. } // { WEGetUndoInfo }
  342.  
  343. pascal long WEGetModCount(WEHandle hWE)
  344. {
  345.     return (*hWE)->modCount;
  346. } // { WEGetModCount }
  347.  
  348. pascal void WEResetModCount(WEHandle hWE)
  349. {
  350.     (*hWE)->modCount = 0;
  351.     WEClearUndo(hWE);
  352. } // { WEResetModCount }
  353.  
  354. pascal void _WEAdjustUndoRange(long moreBytes, WEHandle hWE)
  355. {
  356.     WEActionHandle hAction;
  357.  
  358.     hAction = (WEActionHandle)(*hWE)->hActionStack;
  359.     if (hAction != nil) 
  360.         (*hAction)->delRangeLength = (*hAction)->delRangeLength + moreBytes;
  361.  
  362. } // { _WEAdjustUndoRange }
  363.  
  364. pascal OSErr _WETypeChar(char theByte, WEHandle hWE)
  365. {
  366.     WEPtr pWE;
  367.     DoubleByte db;
  368.     long offset, endOffset, charLength;
  369.     OSErr err;
  370.  
  371.     pWE = *hWE;                    // { the WE record must be already locked }
  372.     charLength = 1;                // { assume 1-byte character by default }
  373.     db.firstByte = theByte;
  374.     offset = pWE->selStart;
  375.  
  376.     // { delete current selection, if any }
  377.     err = _WEDeleteRange(offset, pWE->selEnd, hWE);
  378.     if (err != noErr) 
  379.         goto cleanup2;
  380.     
  381.     pWE->selEnd = offset; //  { needed in case we take a premature exit }
  382.  
  383.     // { make sure the font script is synchronized with the keyboard script }
  384.     _WESynchNullStyle(hWE);
  385.  
  386.     if (BTST(pWE->flags, weFDoubleByte))
  387.     {
  388.  
  389.         // { special processing for double-byte characters }
  390.         if (pWE->firstByte != 0) 
  391.         {
  392.  
  393.             // { if this byte is the second half of a double-byte character, }
  394.             // { insert the two bytes at the same time (flush the double-byte cache) }
  395.             db.firstByte = pWE->firstByte;
  396.             db.secondByte = theByte;
  397.             charLength = 2;
  398.             pWE->firstByte = 0;
  399.         }
  400.         else
  401.         {
  402.  
  403.             // { if theByte is the first half of a double-byte character, just cache it and exit }
  404.             if (CallWECharByteProc(&theByte, 0, FontToScript(pWE->nullStyle.runStyle.tsFont),
  405.                 hWE, (WECharByteUPP)pWE->charByteHook) == smFirstByte)
  406.             {
  407.                 pWE->firstByte = theByte;
  408.                 return noErr;
  409.             }
  410.         }
  411.  
  412.     } // { if double-byte script installed }
  413.  
  414.     // { insert the new character into the text }
  415.     err = _WEInsertText(offset, (Ptr)&db, charLength, hWE);
  416.     if (err != noErr) 
  417.         goto cleanup2;
  418.  
  419.     // { adjust undo buffer for the new character }
  420.     _WEAdjustUndoRange(charLength, hWE);
  421.  
  422.     // { invalid the null style }
  423.     BCLR(pWE->flags, weFUseNullStyle);
  424.  
  425.     // { move the insertion point after the new character }
  426.     endOffset = offset + charLength;
  427.     pWE->selStart = endOffset;
  428.     pWE->selEnd = endOffset;
  429.  
  430.     // { redraw the text }
  431.     err = _WERedraw(offset, endOffset, hWE);
  432.     if (err != noErr) 
  433.         goto cleanup2;
  434.  
  435. cleanup1:
  436.     // { clear result code }
  437.     err = noErr;
  438.  
  439. cleanup2:
  440.     // { return result code }
  441.     return err;
  442.  
  443. } // { _WETypeChar }
  444.  
  445. pascal OSErr _WEBackspace(WEHandle hWE)
  446. {
  447.     // { this routine is called by WEKey to handle the backspace key }
  448.     // { the WE record is guaranteed to be already locked }
  449.  
  450.     WEPtr pWE;
  451.     WEActionPtr pAction;
  452.     long rangeStart, rangeEnd, charLength;
  453.     char *pChars;
  454.     WERunInfo runInfo;
  455.     Boolean saveActionLock;
  456.     OSErr err;
  457.  
  458.     pWE = *hWE;
  459.  
  460.     // { calculate the text range to delete }
  461.     // { if the selection is non-empty, delete that }
  462.     rangeStart = pWE->selStart;
  463.     rangeEnd = pWE->selEnd;
  464.     if (rangeStart == rangeEnd) 
  465.     {
  466.  
  467.         // { otherwise the selection is an insertion point }
  468.         // { do nothing if insertion point is at the beginning of the text }
  469.         if (rangeStart == 0) return noErr; 
  470.  
  471.         // { determine the byte-type of the character preceding the insertion point }
  472.         if (WECharByte(rangeStart - 1, hWE) == smSingleByte) 
  473.             charLength = 1;
  474.         else
  475.             charLength = 2;
  476.         rangeStart = rangeStart - charLength;
  477.  
  478.         if (pWE->hActionStack != nil) 
  479.         {
  480.             // { UNDO SUPPORT FOR BACKSPACES }
  481.  
  482.             // { lock the action record }
  483.             saveActionLock = _WESetHandleLock(pWE->hActionStack, true);
  484.             pAction = *(WEActionHandle)pWE->hActionStack;
  485.  
  486.             // { backspaces over the newly entered text aren't a problem }
  487.             if (pAction->delRangeLength > 0) 
  488.                 pAction->delRangeLength = pAction->delRangeLength - charLength;
  489.             else
  490.             {
  491.  
  492.                 // { the hard part comes when backspacing past the new text because }
  493.                 // { the user is about to delete a character not included in the block we saved }
  494.  
  495.                 // { leng our saved text handle }
  496.                 SetHandleSize(pAction->hText, pAction->insRangeLength + charLength);
  497.                 err = MemError();
  498.                 if (err != noErr) return err;
  499.  
  500.                 // { move old contents forward }
  501.                 pChars = *(char **)pAction->hText;
  502.                 BlockMoveData(pChars, &pChars[charLength], pAction->insRangeLength);
  503.  
  504.                 // { prepend the character to be deleted to the beginning of our saved text handle }
  505.                 pChars[0] = WEGetChar(rangeStart, hWE);
  506.                 if (charLength == 2) 
  507.                     pChars[1] = WEGetChar(rangeStart + 1, hWE);
  508.  
  509.                 // { adjust internal counters }
  510.                 pAction->insRangeLength = pAction->insRangeLength + charLength;
  511.                 pAction->delRangeStart = pAction->delRangeStart - charLength;
  512.  
  513.                 // { get style run info associated with the about-to-be-deleted character }
  514.                 WEGetRunInfo(rangeStart, &runInfo, hWE);
  515.  
  516.                 // { prepend a new style element to our style scrap, if necessary }
  517.                 err = _WEPrependStyle(pAction->hStyles, &runInfo, charLength);
  518.                 if (err != noErr) return err; 
  519.  
  520.                 // { do the same with our object "soup" }
  521.                 err = _WEPrependObject(pAction->hSoup, &runInfo, charLength);
  522.                 if (err != noErr) return err;
  523.             
  524.             } // { if deleting old text }
  525.  
  526.             // { unlock the action record }
  527.             _WESetHandleLock(pWE->hActionStack, saveActionLock);
  528.  
  529.         } // { if undo support is enabled }
  530.     } // { if selection is empty }
  531.  
  532.     err = _WEDeleteRange(rangeStart, rangeEnd, hWE);
  533.     if (err != noErr) return err;
  534.  
  535.     // { keep track of current selection range }
  536.     pWE->selStart = rangeStart;
  537.     pWE->selEnd = rangeStart;
  538.  
  539.     // { redraw the text }
  540.     err = _WERedraw(rangeStart, rangeStart, hWE);
  541.     
  542.     return err;
  543. } // { _WEBackspace }
  544.  
  545. pascal OSErr _WEForwardDelete(WEHandle hWE)
  546. {
  547.     
  548.     // { this routine is called by WEKey to handle the forward delete key }
  549.     // { the WE record is guaranteed to be already locked }
  550.  
  551.     WEPtr pWE;
  552.     WEActionPtr pAction;
  553.     long rangeStart, rangeEnd, charLength;
  554.     WERunInfo runInfo;
  555.     DoubleByte db;
  556.     Boolean saveActionLock;
  557.     OSErr err;
  558.  
  559.     pWE = *hWE;
  560.  
  561.     // { calculate the text range to delete }
  562.     // { if the selection is non-empty, delete that }
  563.     rangeStart = pWE->selStart;
  564.     rangeEnd = pWE->selEnd;
  565.     if (rangeStart == rangeEnd)
  566.     {
  567.         
  568.         // { otherwise the selection is an insertion point }
  569.         // { do nothing if insertion point is at the end of the text }
  570.         if (rangeStart == pWE->textLength)
  571.             return noErr;
  572.  
  573.         // { determine the byte-type of the character following the insertion point }
  574.         if (WECharByte(rangeStart, hWE) == smSingleByte)
  575.             charLength = 1;
  576.         else
  577.             charLength = 2;
  578.         rangeEnd = rangeStart + charLength;
  579.  
  580.         if (pWE->hActionStack != nil)
  581.         {
  582.  
  583.             // { UNDO SUPPORT FOR FORWARD DELETE }
  584.  
  585.             // { lock the action record }
  586.             saveActionLock = _WESetHandleLock(pWE->hActionStack, true);
  587.             pAction = *(WEActionHandle)(pWE->hActionStack);
  588.  
  589.             // { make a copy of the character about to be deleted }
  590.             db.firstByte = WEGetChar(rangeStart, hWE);
  591.             if (charLength == 2)
  592.                 db.secondByte = WEGetChar(rangeStart + 1, hWE);
  593.  
  594.             // { append it to the end of our saved text handle }
  595.             PtrAndHand(&db, pAction->hText, charLength);
  596.             err = MemError();
  597.             if (err != noErr)
  598.                 return err;
  599.  
  600.             // { get style run info associated with the about-to-be-deleted character }
  601.             WEGetRunInfo(rangeStart, &runInfo, hWE);
  602.  
  603.             // { append a new style element to our style scrap, if necessary }
  604.             err = _WEAppendStyle(pAction->hStyles, &runInfo, pAction->insRangeLength);
  605.             if (err != noErr)
  606.                 return err;
  607.  
  608.             // { do the same with our object soup }
  609.             err = _WEAppendObject(pAction->hSoup, &runInfo, pAction->insRangeLength);
  610.             if (err != noErr)
  611.                 return err;
  612.  
  613.             // { adjust internal counters }
  614.             pAction->insRangeLength = pAction->insRangeLength + charLength;
  615.  
  616.             // { unlock the action record }
  617.             _WESetHandleLock(pWE->hActionStack, saveActionLock);
  618.  
  619.         } // { if undo support is enabled }
  620.     } // { if selection is empty }
  621.  
  622.     err = _WEDeleteRange(rangeStart, rangeEnd, hWE);
  623.     if (err != noErr)
  624.         return err;
  625.  
  626.     // { keep track of current selection range }
  627.     pWE->selStart = rangeStart;
  628.     pWE->selEnd = rangeStart;
  629.  
  630.     // { redraw the text }
  631.     err = _WERedraw(rangeStart, rangeStart, hWE);
  632.  
  633.     return err;
  634. } // { _WEForwardDelete }
  635.  
  636. pascal Boolean WEIsTyping(WEHandle hWE)
  637. {
  638.     WEPtr pWE;
  639.  
  640.     // { return TRUE if we're tracking a typing sequence in the specified WE instance }
  641.  
  642.     pWE = *hWE;                    // { the WE record must already be locked }
  643.  
  644.     // { there must be an undo buffer }
  645.     if (pWE->hActionStack == nil) return false;
  646.  
  647.     // { the action kind must be "typing" and the redo flag must be clear }
  648.     if ((*(WEActionHandle)pWE->hActionStack)->actionKind != weAKTyping)
  649.         return false;
  650.     if (((*(WEActionHandle)pWE->hActionStack)->actionFlags & weAFIsRedo) != 0) 
  651.         return false;
  652.  
  653.     // { finally, the selection range mustn't have moved since the last WEKey }
  654.     if ((pWE->selStart == pWE->selEnd) && (pWE->selStart == 
  655.         (*(WEActionHandle)pWE->hActionStack)->delRangeStart + 
  656.         (*(WEActionHandle)pWE->hActionStack)->delRangeLength)) 
  657.         return true;
  658.     
  659.     return false;
  660. } // { _WEIsTyping }
  661.  
  662. pascal void WEKey(short key, short modifiers, WEHandle hWE)
  663. {
  664.     WEPtr pWE;
  665.     WEActionHandle hAction;
  666.     Boolean saveWELock;
  667.     OSErr err;
  668.     
  669.     err = noErr;
  670.  
  671.     // { lock the WE record }
  672.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  673.     pWE = *hWE;
  674.  
  675.     // { hide the caret if it's showing }
  676.     if (BTST(pWE->flags, weFCaretVisible))
  677.         _WEBlinkCaret(hWE);
  678.  
  679.     // { hide the cursor (it will show again as soon as it's moved) }
  680.     ObscureCursor();
  681.  
  682.     // { dispatch on key class (arrow keys, printable characters, backspace) }
  683.     if ((key >= kArrowLeft) && (key <= kArrowDown)) 
  684.             _WEDoArrowKey(key, modifiers, hWE);
  685.     else
  686.     {
  687.  
  688.         // { non-arrow keys modify the text, so make sure editing is allowed }
  689.         if (!BTST(pWE->flags, weFReadOnly))
  690.         {
  691.             // { are we tracking a typing sequence? }
  692.             if (WEIsTyping(hWE) == false)
  693.             {
  694.                 // { nope;  start a new one }
  695.                 // { increment modification count }
  696.                 pWE->modCount++;
  697.  
  698.                 // if undo support is enabled, create a new action to keep track of typing
  699.                 if (BTST(pWE->flags, weFUndoSupport))
  700.                 {
  701.                     WEClearUndo(hWE);
  702.                     if (WENewAction(pWE->selStart, pWE->selEnd, 0, weAKTyping, 0, hWE, &hAction) == noErr) 
  703.                         if (WEPushAction(hAction) != noErr)
  704.                         {
  705.                             ;
  706.                         }
  707.                 }
  708.             } // { if WEIsTyping }
  709.  
  710.             if (key == kBackspace) 
  711.                 err = _WEBackspace(hWE);
  712.             else if (key == kForwardDelete)
  713.                 err = _WEForwardDelete(hWE);
  714.             else
  715.                 err = _WETypeChar(key, hWE);
  716.         } //  { if not read-only }
  717.         
  718.     }
  719.  
  720.     // { unlock the WE record }
  721.     _WESetHandleLock((Handle)hWE, saveWELock);
  722.  
  723. } // { WEKey }
  724.  
  725. pascal OSErr WEInsert(Ptr textPtr, long textLength, StScrpHandle hStyles, Handle hSoup, WEHandle hWE)
  726. {
  727.     WEPtr pWE;
  728.     long offset, endOffset;
  729.     WEActionHandle hAction;
  730.     short intPasteAction;
  731.     Boolean saveWELock;
  732.     char space;
  733.     OSErr err;
  734.  
  735.     // { lock the WE record }
  736.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  737.     pWE = *hWE;
  738.     offset = pWE->selStart;
  739.  
  740.     // { return an error code if this instance is read-only }
  741.     err = weReadOnlyErr;
  742.     if (BTST(pWE->flags, weFReadOnly))
  743.         goto cleanup;
  744.  
  745.     // { stop any ongoing inline input session }
  746.     WEStopInlineSession(hWE);
  747.  
  748.     // { increment modification count }
  749.     pWE->modCount = pWE->modCount + 1;
  750.  
  751.     // { if undo support is enabled, save current selection range }
  752.     if (BTST(pWE->flags, weFUndoSupport)) 
  753.     {
  754.         WEClearUndo(hWE);
  755.         if (WENewAction(offset, pWE->selEnd, textLength, weAKUnspecified, 0, hWE, &hAction) == noErr) 
  756.             if (WEPushAction(hAction) != noErr) 
  757.                 { ;    }
  758.     }
  759.  
  760.     // { delete current selection }
  761.     err = _WEDeleteRange(offset, pWE->selEnd, hWE);
  762.     if (err != noErr) 
  763.         goto cleanup;
  764.  
  765.     // { insert the new text at the insertion point }
  766.     err = _WEInsertText(offset, textPtr, textLength, hWE);
  767.     if (err != noErr) 
  768.         goto cleanup;
  769.     endOffset = offset + textLength;
  770.  
  771.     if (hStyles != nil) 
  772.     {
  773.  
  774.         // { if a style scrap was supplied, apply it to the newly inserted text }
  775.         err = _WEApplyStyleScrap(offset, endOffset, hStyles, hWE);
  776.         if (err != noErr) 
  777.             goto cleanup;
  778.     }
  779.  
  780.     if (hSoup != nil)
  781.     {
  782.  
  783.         // { if an object soup was supplied, apply it to the newly inserted text }
  784.         err = _WEApplySoup(offset, hSoup, hWE);
  785.         if (err != noErr) 
  786.             goto cleanup;
  787.     }
  788.  
  789.     // { determine whether an extra space should be added before or after the inserted text }
  790.     intPasteAction = _WEIntelligentPaste(offset, endOffset, hWE);
  791.  
  792.     // { add the extra space, if necessary }
  793.     if (intPasteAction != weDontAddSpaces) 
  794.     {
  795.  
  796.         space = kSpace;
  797.         if (intPasteAction == weAddSpaceOnLeftSide) 
  798.             err = _WEInsertText(offset, &space, 1, hWE);
  799.         else
  800.             err = _WEInsertText(endOffset, &space, 1, hWE);
  801.         if (err != noErr) 
  802.             goto cleanup;
  803.         endOffset = endOffset + 1;
  804.  
  805.         // { adjust undo buffer (if any) for the extra space }
  806.         _WEAdjustUndoRange(1, hWE);
  807.  
  808.     }
  809.  
  810.     // { invalid the null style }
  811.     BCLR(pWE->flags, weFUseNullStyle);
  812.  
  813.     // { move the insertion point at the end of the inserted text }
  814.     pWE->selStart = endOffset;
  815.     pWE->selEnd = endOffset;
  816.  
  817.     // { redraw the text }
  818.     err = _WERedraw(offset, endOffset, hWE);
  819.     if (err != noErr) 
  820.         goto cleanup;
  821.  
  822.     // { clear result code }
  823.     err = noErr;
  824.  
  825. cleanup:
  826.     // { unlock the WE record }
  827.     _WESetHandleLock((Handle)hWE, saveWELock);
  828.  
  829.     // { return result code }
  830.     return err;
  831. } // { WEInsert }
  832.  
  833. pascal OSErr WEInsertObject(OSType objectType, Handle objectDataHandle, Point objectSize, WEHandle hWE)
  834. {
  835.     WEPtr pWE;
  836.     WEActionHandle hAction;
  837.     long offset, endOffset;
  838.     WETextStyle ts;
  839.     char marker;
  840.     Boolean saveWELock;
  841.     OSErr err;
  842.  
  843.     _WEBlockClr((Ptr)&ts, sizeof(ts));
  844.  
  845.     // { lock the WE record }
  846.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  847.     pWE = *hWE;
  848.     offset = pWE->selStart;
  849.  
  850.     // { return an error code if this instance is read-only }
  851.     err = weReadOnlyErr;
  852.     if (BTST(pWE->flags, weFReadOnly))
  853.         goto cleanup;
  854.  
  855.     // { stop any ongoing inline input session }
  856.     WEStopInlineSession(hWE);
  857.  
  858.     // { call the 'new' handler to initialize private object storage (if any) }
  859.     // { and to calculate the default size for this object }
  860.  
  861.     err = _WENewObject(objectType, objectDataHandle, hWE, (WEObjectDescHandle *)&ts.tsObject);
  862.     if (err != noErr) 
  863.         goto cleanup;
  864.  
  865.     // { use the specified object size, unless it is (0, 0), in which case keep the default size }
  866.     if (*((long *)&objectSize) != 0) 
  867.         (*(WEObjectDescHandle)ts.tsObject)->objectSize = objectSize;
  868.  
  869.     // { increment modification count }
  870.     pWE->modCount = pWE->modCount + 1;
  871.  
  872.     // { if undo support is enabled, save current selection range }
  873.     if (BTST(pWE->flags, weFUndoSupport)) 
  874.     {
  875.         WEClearUndo(hWE);
  876.         if (WENewAction(offset, pWE->selEnd, 1, weAKUnspecified, 0, hWE, &hAction) == noErr) 
  877.             if (WEPushAction(hAction) != noErr) 
  878.                 { ; }
  879.     }
  880.  
  881.     // { delete current selection }
  882.     err = _WEDeleteRange(offset, pWE->selEnd, hWE);
  883.     if (err != noErr) 
  884.         goto cleanup;
  885.  
  886.     // { insert a kObjectMarker character at the insertion point }
  887.     marker = kObjectMarker;
  888.     err = _WEInsertText(offset, &marker, 1, hWE);
  889.     if (err != noErr) 
  890.         goto cleanup;
  891.  
  892.     // { move the insertion point after the inserted text }
  893.     endOffset = offset + 1;
  894.     pWE->selStart = endOffset;
  895.     pWE->selEnd = endOffset;
  896.  
  897.     // { record a reference to the object descriptor in the style table }
  898.     err = _WESetStyleRange(offset, endOffset, weDoObject, &ts, hWE);
  899.     ts.tsObject = kNullObject;
  900.     if (err != noErr) 
  901.         goto cleanup;
  902.  
  903.     // { invalid the null style }
  904.     BCLR(pWE->flags, weFUseNullStyle);
  905.  
  906.     // { redraw the text }
  907.     err = _WERedraw(offset, endOffset, hWE);
  908.     if (err != noErr) 
  909.         goto cleanup;
  910.  
  911.     // { clear result code }
  912.     err = noErr;
  913.  
  914. cleanup:
  915.     // { clean up }
  916.     _WEForgetHandle((Handle *)&ts.tsObject);
  917.  
  918.     // { unlock the WE record }
  919.     _WESetHandleLock((Handle)hWE, saveWELock);
  920.  
  921.     // { return result code }
  922.     return err;
  923. } // { WEInsertObject }
  924.  
  925. pascal OSErr WEDelete(WEHandle hWE)
  926. {
  927.     WEPtr pWE;
  928.     WEActionHandle hAction;
  929.     long rangeStart, rangeEnd;
  930.     Boolean saveWELock;
  931.     OSErr err;
  932.  
  933.     // { lock the WE record }
  934.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  935.     pWE = *hWE;
  936.  
  937.     // { return an error code if this instance is read-only }
  938.     err = weReadOnlyErr;
  939.     if (BTST(pWE->flags, weFReadOnly))
  940.         goto cleanup;
  941.  
  942.     // { stop any ongoing inline input session }
  943.     WEStopInlineSession(hWE);
  944.  
  945.     // { get current selection range }
  946.     rangeStart = pWE->selStart;
  947.     rangeEnd = pWE->selEnd;
  948.  
  949.     // { do nothing if the selection range is empty }
  950.     if (rangeStart < rangeEnd) 
  951.     {
  952.  
  953.         // { increment modification count }
  954.         pWE->modCount = pWE->modCount + 1;
  955.  
  956.         // { range extension for intelligent cut-and-paste }
  957.         _WEIntelligentCut(&rangeStart, &rangeEnd, hWE);
  958.  
  959.         // { if undo support is enabled, save the range to be deleted }
  960.         if (BTST(pWE->flags, weFUndoSupport)) 
  961.         {
  962.             WEClearUndo(hWE);
  963.             if (WENewAction(rangeStart, rangeEnd, 0, weAKClear, 0, hWE, &hAction) == noErr) 
  964.                 if (WEPushAction(hAction) != noErr) 
  965.                     { ; }
  966.         }
  967.  
  968.         // { delete the selection range }
  969.         err = _WEDeleteRange(rangeStart, rangeEnd, hWE);
  970.         if (err != noErr) 
  971.             goto cleanup;
  972.  
  973.         // { reset the selection range }
  974.         pWE->selStart = rangeStart;
  975.         pWE->selEnd = rangeStart;
  976.  
  977.         // { redraw the text }
  978.         err = _WERedraw(rangeStart, rangeStart, hWE);
  979.         if (err != noErr) 
  980.             goto cleanup;
  981.  
  982.     } // { if non-empty selection }
  983.  
  984.     // { clear result code }
  985.     err = noErr;
  986.  
  987. cleanup:
  988.     // { unlock the WE record }
  989.     _WESetHandleLock((Handle)hWE, saveWELock);
  990.  
  991.     // { return result code }
  992.     return err;
  993. } // { WEDelete }
  994.  
  995. pascal OSErr WECut(WEHandle hWE)
  996. {
  997.     OSErr err;
  998.  
  999.     // { first copy... }
  1000.     err = WECopy(hWE);
  1001.     if (err != noErr) return err;
  1002.  
  1003.     // { ... then delete }
  1004.     err = WEDelete(hWE);
  1005.     if (err != noErr) return err;
  1006.  
  1007.     // { change the action kind of the most recent action, if any }
  1008.     if ((*hWE)->hActionStack != nil) 
  1009.         (*(WEActionHandle)(*hWE)->hActionStack)->actionKind = weAKCut;
  1010.  
  1011.     return noErr;
  1012. }  // { WECut }
  1013.  
  1014. pascal Boolean WECanPaste(WEHandle hWE)
  1015. {
  1016.     long scrapOffset;
  1017.     OSType objectType;
  1018.     short index;
  1019.  
  1020.     if (!BTST((*hWE)->flags, weFReadOnly))
  1021.     {
  1022.         // { return TRUE if the desk scrap contains a text flavor }
  1023.         if (GetScrap(nil, kTypeText, &scrapOffset) > 0) return true;
  1024.  
  1025.         // { see if the desk scrap contains a flavor matching one of the registered object types }
  1026.         index = 0;
  1027.         while (_WEGetIndObjectType(index, &objectType, hWE) == noErr)
  1028.         {
  1029.             if (GetScrap(nil, objectType, &scrapOffset) > 0) return true;
  1030.             index = index + 1;
  1031.         } // { while }
  1032.     }
  1033.     return false;
  1034. } // { WECanPaste }
  1035.  
  1036. pascal OSErr WEPaste(WEHandle hWE)
  1037. {
  1038.     Handle hItem;
  1039.     Handle hStyles;
  1040.     Handle hSoup;
  1041.     long selStart;
  1042.     long scrapOffset;
  1043.     OSType objectType;
  1044.     short index;
  1045.     OSErr err;
  1046.     Point zeroPoint = {0, 0};
  1047.  
  1048.     hItem = nil;
  1049.     hStyles = nil;
  1050.     hSoup = nil;
  1051.     selStart = (*hWE)->selStart;
  1052.  
  1053.     // { allocate a handle to hold a scrap item }
  1054.     err = _WEAllocate(0, kAllocTemp, &hItem);
  1055.     if (err != noErr) 
  1056.         goto cleanup;
  1057.  
  1058.     // { look for a text flavor }
  1059.     if (GetScrap(hItem, kTypeText, &scrapOffset) <= 0) 
  1060.     {
  1061.  
  1062.         // { no text: look for a flavor matching one of the registered object types }
  1063.         index = 0;
  1064.         while (_WEGetIndObjectType(index, &objectType, hWE) == noErr)
  1065.         {
  1066.             if (GetScrap(hItem, objectType, &scrapOffset) > 0) 
  1067.             {
  1068.             
  1069.                 // { found a registered type: create a new object out of the tagged data }
  1070.                 err = WEInsertObject(objectType, hItem, zeroPoint, hWE);
  1071.  
  1072.                 // { if successful, set hItem to NIL so clean-up section won't kill the object data }
  1073.                 if (err == noErr) 
  1074.                     hItem = nil;
  1075.                 goto cleanup;
  1076.             }
  1077.  
  1078.             // { try with next flavor }
  1079.             index = index + 1;
  1080.         } // { while }
  1081.  
  1082.         // { nothing pasteable: return an error code }
  1083.         err = noTypeErr;
  1084.         goto cleanup;
  1085.     }
  1086.  
  1087.     // { allocate a handle to hold the style scrap, if any }
  1088.     err = _WEAllocate(0, kAllocTemp, &hStyles);
  1089.     if (err != noErr) 
  1090.         goto cleanup;
  1091.  
  1092.     // { look for a 'styl' item accompanying the text }
  1093.     if (GetScrap(hStyles, kTypeStyles, &scrapOffset) <= 0) 
  1094.         // { forget the handle if nothing was found or an error occurred }
  1095.         _WEForgetHandle(&hStyles);
  1096.  
  1097.     // { allocate a handle to hold the soup, if any }
  1098.     err = _WEAllocate(0, kAllocTemp, &hSoup);
  1099.     if (err != noErr) 
  1100.         goto cleanup;
  1101.  
  1102.     // { look for a 'SOUP' item accompanying the text }
  1103.     if (GetScrap(hSoup, kTypeSoup, &scrapOffset) <= 0) 
  1104.         // { forget the handle if nothing was found or an error occurred }
  1105.         _WEForgetHandle(&hSoup);
  1106.  
  1107.     // { lock down the text }
  1108.     HLock(hItem);
  1109.  
  1110.     // { insert the text }
  1111.     err = WEInsert(*hItem, GetHandleSize(hItem), (StScrpHandle)hStyles, hSoup, hWE);
  1112.  
  1113. cleanup:
  1114.     // { if successful, change the action kind of the most recent action, if any }
  1115.     if (err == noErr) 
  1116.         if ((*hWE)->hActionStack != nil) 
  1117.             (*(WEActionHandle)(*hWE)->hActionStack)->actionKind = weAKPaste;
  1118.  
  1119.     // { clean up }
  1120.     _WEForgetHandle(&hItem);
  1121.     _WEForgetHandle(&hStyles);
  1122.     _WEForgetHandle(&hSoup);
  1123.  
  1124.     // { return result code }
  1125.     return err;
  1126. } // { WEPaste }
  1127.  
  1128. pascal OSErr WESetStyle(short mode, TextStyle *ts, WEHandle hWE)
  1129. {
  1130.     WEPtr pWE;
  1131.     WEActionHandle hAction;
  1132.     ScriptCode fontScript;
  1133.     Boolean saveWELock;
  1134.     OSErr err;
  1135.  
  1136.     // { lock the WE record }
  1137.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  1138.     pWE = *hWE;
  1139.  
  1140.     // { return an error code if this instance is read-only }
  1141.     err = weReadOnlyErr;
  1142.     if (BTST(pWE->flags, weFReadOnly))
  1143.         goto cleanup;
  1144.  
  1145.     // { stop any ongoing inline input session }
  1146.     WEStopInlineSession(hWE);
  1147.  
  1148.     if (pWE->selStart == pWE->selEnd) 
  1149.     {
  1150.  
  1151.         // { NULL SELECTION }
  1152.         // { first make sure the nullStyle field contains valid information }
  1153.         _WESynchNullStyle(hWE);
  1154.  
  1155.         // { apply style changes to the nullStyle record }
  1156.         _WECopyStyle((WETextStyle *)ts, &pWE->nullStyle.runStyle, pWE->nullStyle.runStyle.tsFace, mode);
  1157.  
  1158.         // { if the font was altered, synchronize the keyboard script }
  1159.         if (BTST(pWE->flags, weFNonRoman)) 
  1160.             if (BTST(mode, kModeFont)) 
  1161.             {
  1162.                 fontScript = FontToScript(pWE->nullStyle.runStyle.tsFont);
  1163.                 if (fontScript != GetScriptManagerVariable(smKeyScript)) 
  1164.                     KeyScript(fontScript);
  1165.             }
  1166.     }
  1167.     else
  1168.     {
  1169.         // { NON-EMPTY SELECTION }
  1170.  
  1171.         // { increment modification count }
  1172.         pWE->modCount = pWE->modCount + 1;
  1173.  
  1174.         // { if undo support is enabled, save the styles of the text range to be affected }
  1175.         if (BTST(pWE->flags, weFUndoSupport)) 
  1176.         {
  1177.             WEClearUndo(hWE);
  1178.             if (WENewAction(pWE->selStart, pWE->selEnd, pWE->selEnd - pWE->selStart, weAKSetStyle,
  1179.                 weAFDontSaveText + weAFDontSaveSoup, hWE, &hAction) == noErr) 
  1180.                 if (WEPushAction(hAction) != noErr) 
  1181.                     { ; }
  1182.         }
  1183.  
  1184.         // { set the style of the selection range }
  1185.         err = _WESetStyleRange(pWE->selStart, pWE->selEnd, mode, (WETextStyle *)ts, hWE);
  1186.         if (err != noErr) 
  1187.             goto cleanup;
  1188.  
  1189.         // { and redraw the text }
  1190.         err = _WERedraw(pWE->selStart, pWE->selEnd, hWE);
  1191.         if (err != noErr) 
  1192.             goto cleanup;
  1193.     }
  1194.  
  1195.     // { clear the result code }
  1196.     err = noErr;
  1197.  
  1198. cleanup:
  1199.     // { unlock the WE record }
  1200.     _WESetHandleLock((Handle)hWE, saveWELock);
  1201.  
  1202.     // { return result code }
  1203.     return err;
  1204. } // { WESetStyle }
  1205.  
  1206. pascal OSErr WEUseStyleScrap(StScrpHandle hStyles, WEHandle hWE)
  1207. {
  1208.     WEPtr pWE;
  1209.     Boolean saveWELock;
  1210.     OSErr err;
  1211.  
  1212.     // { lock the WE record }
  1213.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  1214.     pWE = *hWE;
  1215.  
  1216.     // { return an error code if this instance is read-only }
  1217.     err = weReadOnlyErr;
  1218.     if (BTST(pWE->flags, weFReadOnly))
  1219.         goto cleanup;
  1220.  
  1221.     // { apply the style scrap to the selection range }
  1222.     err = _WEApplyStyleScrap(pWE->selStart, pWE->selEnd, hStyles, hWE);
  1223.     if (err != noErr) 
  1224.         goto cleanup;
  1225.  
  1226.     // { redraw the text }
  1227.     err = _WERedraw(pWE->selStart, pWE->selEnd, hWE);
  1228.  
  1229. cleanup:
  1230.     // { unlock the WE record }
  1231.     _WESetHandleLock((Handle)hWE, saveWELock);
  1232.  
  1233.     // { return result code }
  1234.     return err;
  1235. }
  1236.